import heapq
import json

from querier.esquerier import ElasticSearchQuerier
import utils.utils as utils
import math
import operator
import re

MINIMUM_SHOULD_MATCH = '5<85% 10<9'
MAX_BRANDS = 40


class WechatKOLSearchWords(ElasticSearchQuerier):
    def __init__(self, es, index, doc_type, nlp_service=None):
        super(WechatKOLSearchWords, self).__init__(es, index, doc_type)
        self.nlp_service = nlp_service

    def _build_query(self, args):
        """
        从args创建查询
        :param args:
        :return:
        """

        term = args.get('term', '')
        term = term if term else ''
        filters = args.get('filters', {})
        filters = filters if filters else {}
        order = args.get('order_by', utils.ORDER_OVERALL)
        order = order if order else utils.ORDER_OVERALL
        from_ = args.get('from', 0)
        from_ = from_ if from_ else 0
        size_ = args.get('size', 1000)
        size_ = size_ if size_ else 1000
        highlight = args.get('highlight', False)
        highlight = highlight if highlight in (True, False) else False
        min_relative = args.get('min_relative', 0.0)
        min_relative = min_relative if min_relative else 0.0

        max_relative = args.get('max_relative', 20.0)
        max_relative = max_relative if max_relative else 20.0

        # 处理查询文本
        term2, keywords, ex_keywords, weights = utils.process_query_term(term, self.nlp_service)
        ex_kw = utils.get_kv_json(ex_keywords, weights)
        ex_category = self.nlp_service.classify(keywords)
        ex_category = ex_category.get('classify', {})
        ex_category = utils.get_kv_json(ex_category.get('category', []), ex_category.get('category_prob', []))
        query = self._genQuery(' '.join(keywords), filters, order, from_, size_, highlight, min_relative, max_relative)
        return query, {}, {'keywords': keywords, 'order': order, 'ex_keywords': ex_kw, "ex_category": ex_category}

    def _build_result(self, es_result, param):
        keywords = param['keywords']
        order = param['order']
        total = es_result['hits']['total']
        wechat = []

        for hit in es_result['hits']['hits']:
            wechat.append(self.extractResult(hit, order, keywords))
        return {
            'total': total,
            'keywords': keywords,
            'wechat': wechat,
            'ex_keywords': param.get('ex_keywords', []),
            'ex_category': param.get('ex_category', [])
        }

    def _genQuery(self, query_keywords, filters, order, from_, size_, highlight, min_relative, max_relative):
        must_clause = []
        if max_relative >= 1.0:
            max_relative = 20.0
        min_score = math.pow(2, 2 * min_relative) - 1.0
        max_score = math.pow(2, 2 * max_relative) - 1.0

        filter_clause = []
        if filters:
            if filters.get('category'):
                filters['category'] = [utils.category_smzdm_2_encode(c) for c in filters['category']]
                filter_clause = self._addFilterClause(filter_clause, filters, 'category', 'should')

            if filters.get('category_media'):
                filters['category_media'] = [utils.category_media_2_encode(c) for c in filters['category_media']]
                # filters['category_media_weight'] = [CATEGORY_CUTOFF]
                filter_clause = self._addFilterClause(filter_clause, filters, 'category_media', 'should')
                filter_clause = self._addFilterRangeClause(filter_clause, filters, 'category_media_weight')

            filter_clause = self._addFilterClause(filter_clause, filters, 'keywords')

            # filter_clause = self._add_filter_clause(filter_clause, filters, 'category', 'should')
            # filter_clause = self._add_filter_clause(filter_clause, filters, 'brands', 'should')
            filter_clause = self._addFilterClause(filter_clause, filters, 'customer_type', 'should')
            filter_clause = self._addFilterRangeClause(filter_clause, filters, 'sum_read_num')
            filter_clause = self._addFilterRangeClause(filter_clause, filters, 'avg_read_num')
            filter_clause = self._addFilterRangeClause(filter_clause, filters, 'max_read_num')
            filter_clause = self._addFilterRangeClause(filter_clause, filters, 'sum_like_num')
            filter_clause = self._addFilterRangeClause(filter_clause, filters, 'avg_like_num')
            filter_clause = self._addFilterRangeClause(filter_clause, filters, 'max_like_num')
            filter_clause = self._addFilterRangeClause(filter_clause, filters, 'like_read_ratio')
            filter_clause = self._addFilterRangeClause(filter_clause, filters, 'timestamp')
            filter_clause = self._addFilterRangeClause(filter_clause, filters, 'score')
            filter_clause = self._addFilterMatch(filter_clause, filters, 'biz_code')
            filter_clause = self._addFilterMatch(filter_clause, filters, 'biz_name_seg')
            filter_clause = self._addFilterMatch(filter_clause, filters, 'biz_info_seg')

        query = {"query": {"bool": {}}}
        if filter_clause:
            query['query']['bool']['filter'] = filter_clause

        if query_keywords:
            query['query']['bool']['must'] = {
                'bool': {
                    'should': [
                        {
                            'match': {
                                'biz_name_seg': {
                                    'analyzer': 'whitespace',
                                    'query': query_keywords,
                                    'boost': 20

                                }
                            }
                        },
                        {
                            'match': {
                                'biz_info_seg': {
                                    'analyzer': 'whitespace',
                                    'query': query_keywords,
                                    'boost': 20

                                }
                            }
                        },
                        {
                            'match': {
                                'keywords': {
                                    'analyzer': 'whitespace',
                                    'query': query_keywords,
                                    'boost': 5
                                }

                            }
                        }
                        # {
                        #     'multi_match': {
                        #         'analyzer': 'whitespace',
                        #         'query': query_keywords,
                        #         'fields': ['biz_name_seg', 'biz_info_seg', 'keywords']
                        #
                        #     }
                        # }
                    ]
                }
            }

        if order == utils.ORDER_INFLUENCE:
            query['sort'] = [
                {'score': 'desc'},
                '_score'
            ]
        elif order == utils.ORDER_RELATIVE:
            query['sort'] = [
                '_score',
                {'score': 'desc'}
            ]
        elif order == 'like_read_ratio':
            query['sort'] = [
                {'like_read_ratio': 'desc'},
                '_score'
            ]
        elif order == 'avg_read_num':
            query['sort'] = [
                {'avg_read_num': 'desc'},
                '_score'
            ]
        elif order == 'avg_like_num':
            query['sort'] = [
                {'avg_like_num': "desc"},
                '_score'
            ]
        elif order == 'max_read_num':
            query['sort'] = [
                {'max_read_num': 'desc'},
                '_score'
            ]
        elif order == 'max_like_num':
            query['sort'] = [
                {'max_like_num': 'desc'},
                '_score'
            ]
        else:
            query['boost_mode'] = 'replace'
            query['script_score'] = {
                "script": {
                    'lang': "painless",
                    'params': {'min_relative': min_score, 'max_relative': max_score},
                    'inline': "if (_score > params.min_relative && _score < params.max_relative) {return Math.log(_score + 1.0) * (doc.score.value+ 0.001)} else {return 0}"
                }

            }
            query = {
                'query': {
                    'function_score': query
                },
                'sort': [
                    '_score',
                    {'score': 'desc'}
                ]
            }

        query['from'] = from_
        query['size'] = size_
        if highlight:
            query['highlight'] = {
                "pre_tags": ["<span class='keyword'>"],
                "post_tags": ["</span>"],
                "fields": {"keywords": {}, "biz_info_seg": {}, "biz_name_seg": {}}
            }

        if min_score > 0.0:
            query['min_score'] = min_score
        return query

    def _addFilterMatch(self, must_clause, filters, key):
        if key in filters:
            if filters[key]:
                clause = []
                must_clause.append({
                    'bool': {'should': clause}
                })
                values = filters[key]
                if isinstance(values, str):
                    values = values.split(' ')
                for fk in values:
                    clause.append({'match': {key: {'query': fk, 'minimum_should_match': '10<85% 20<10'}}})
        return must_clause

    def _addFilterClause(self, filter_clause, filters, key, cond='must'):
        if key in filters:
            if filters[key]:
                clause = []
                filter_clause.append({
                    'bool': {
                        cond: clause
                    }
                })
                for fk in filters[key]:
                    clause.append({'term': {key: fk}})
        return filter_clause

    def _addFilterRangeClause(self, filter_clause, filters, key):
        if key in filters:
            if filters[key]:
                clause = []
                filter_clause.append({
                    'bool': {
                        'must': clause
                    }
                })
                fk = filters[key]
                if not isinstance(fk, list) or len(fk) < 1:
                    pass
                else:
                    min_fk = fk[0]
                    if len(fk) >= 2:
                        max_fk = fk[1]
                    else:
                        max_fk = None
                    if min_fk is not None and min_fk != 'null':
                        clause.append({'range': {key: {"gte": min_fk}}})
                    if max_fk is not None and max_fk != 'null':
                        clause.append({'range': {key: {"lte": max_fk}}})
        return filter_clause

    def extractResult(self, hit, order, in_keywords=[]):
        source_ = hit['_source']
        biz_info = source_['biz_info']

        kol_avatar_url = source_['head_img']
        kol_influence_score = source_['score']
        biz_code = source_['biz_code']
        biz_name = source_['biz_name']
        qrcode_url = "http://mp.weixin.qq.com/mp/qrcode?scene=10000004&size=310&__biz=" + source_['bid']
        score_ = hit['_score'] / (len(in_keywords) + 1)
        sum_like_num = source_['sum_like_num']
        article_count = source_['article_count']
        if article_count ==0 :
           article_like_ratio = 0.0
        else:
            article_like_ratio = float(sum_like_num/int(article_count))

        if order == utils.ORDER_RELATIVE:
            kol_relative = score_
        elif order == utils.ORDER_INFLUENCE:
            kol_relative = score_
        else:
            kol_relative = math.exp(score_ / (kol_influence_score + 1)) - 1.0
        # kol_relative = round(kol_relative, 3)

        # normalize kol_relative
        kol_relative = math.log2(1.0 + kol_relative) / 2
        # kol_relative = round(min(math.log10(1 + kol_relative + 0.0001) / math.log10(1+ 4 +0.0001), 1.0), 3)
        kol_relative = min(kol_relative, 1.0)
        if len(in_keywords) == 0:
            kol_relative = 1.0

        customer_type_dict = {
            None: "无",
            0: "未认证或未知",
            1: "自媒体",
            2: "发行单位，如报社等",
            3: "政府机关",
            4: "基金会等机构",
            5: "民办非企业单位",
            6: "医院寺庙基金会等机构",
            7: "商户旗舰店等",
            8: "社会团体",
            9: "主管单位",
            10: "名人",
            11: "事业单位",
            12: "个体商户"
        }

        keywords = source_['keywords']
        term_in_keywords = [i for i in keywords if i in in_keywords]
        len_term_in_keywords = len(term_in_keywords)
        customer_type = customer_type_dict[source_.get('customer_type')]
        orig_ratio = source_['orig_ratio']
        keywords_weight = source_['keywords_weight']
        orig = source_.get('orig')
        return {
            'biz_name': biz_name,
            'biz_code': biz_code,
            'biz_info': biz_info,
            'qrcode_url': qrcode_url,
            'keywords':  keywords,
            'keywords_weight' : keywords_weight,
            'head_img': kol_avatar_url,
            'score': kol_influence_score,
            'relative': kol_relative,
            'sum_read_num': source_['sum_read_num'],
            'avg_read_num': source_['avg_read_num'],
            'max_read_num': source_['max_read_num'],
            'like_read_ratio': source_.get('like_read_ratio'),
            'categories': source_.get('categories', [])[0:5],
            'timestamp': source_.get('timestamp'),
            'brands': self.nlp_service.get_brands(source_.get('keywords', [])),
            'term_in_keywords' : term_in_keywords,
            'len_term_in_keywords' : len_term_in_keywords,
            'customer_type' : customer_type,
            'article_count': article_count,
            'article_like_ratio': article_like_ratio,
            'orig': orig,
            'orig_ratio' : orig_ratio
        }

def extract_emphasis_keywords(highlight):
    res = ""
    for k in highlight.keys():
        res += ' '.join(highlight[k])
    kw = res.split('<em>')
    if len(kw) <= 1:
        ret = []
    else:
        ret = [x.split('</em>')[0] for x in kw[1:]]
    return list(set(ret))


